﻿/* ****************************************************************
 * author	:	ShianYuan
 * email	:	a-shyuan@microsoft.com, sayuan@outlook.com
 * clr		:	4.0.30319.34014
 * itemname	:	KeyboardHook
 * history	:	created by ShianYuan 2014/9/24 14:23:11 
 * ****************************************************************/

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace TranslateApp.Hook {
    #region 基础类型
    /// <summary>
    /// Window相关消息
    /// </summary>
    public class WindowMessage {
        public const int WM_KEYDOWN = 0x100;  //按下键  
        public const int WM_KEYUP = 0x101; //按键抬起  
        public const int WM_SYSKEYDOWN = 0x104;
        public const int WM_SYSKEYUP = 0x105;
        public const int WM_MOUSEMOVE = 0x200;
        public const int WM_LBUTTONDOWN = 0x201;
        public const int WM_RBUTTONDOWN = 0x204;
        public const int WM_MBUTTONDOWN = 0x207;
        public const int WM_LBUTTONUP = 0x202;
        public const int WM_RBUTTONUP = 0x205;
        public const int WM_MBUTTONUP = 0x208;
        public const int WM_LBUTTONDBLCLK = 0x203;
        public const int WM_RBUTTONDBLCLK = 0x206;
        public const int WM_MBUTTONDBLCLK = 0x209;
    }
    /// <summary>
    /// Hook类型
    /// </summary>
    public enum HookType : int {
        WH_JOURNALRECORD = 0,
        WH_JOURNALPLAYBACK = 1,
        WH_KEYBOARD = 2,
        WH_GETMESSAGE = 3,
        WH_CALLWNDPROC = 4,
        WH_CBT = 5,
        WH_SYSMSGFILTER = 6,
        WH_MOUSE = 7,
        WH_HARDWARE = 8,
        WH_DEBUG = 9,
        WH_SHELL = 10,
        WH_FOREGROUNDIDLE = 11,
        WH_CALLWNDPROCRET = 12,
        WH_KEYBOARD_LL = 13,
        WH_MOUSE_LL = 14
    }
    /// <summary>
    /// 
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct KBDLLHOOKSTRUCT {
        public uint vkCode;
        public uint scanCode;
        public KBDLLHOOKSTRUCTFlags flags;
        public uint time;
        public UIntPtr dwExtraInfo;
    }
    [Flags]
    public enum KBDLLHOOKSTRUCTFlags : uint {
        LLKHF_EXTENDED = 0x01,
        LLKHF_INJECTED = 0x10,
        LLKHF_ALTDOWN = 0x20,
        LLKHF_UP = 0x80,
    }
    /// <summary>
    /// 键盘按键事件
    /// </summary>
    public class HookEventArgs : EventArgs {
        // using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps  
        // to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not  
        public Keys Key;
        public bool Alt;
        public bool Ctrl;
        public bool Shift;

        public HookEventArgs(UInt32 keyCode) {
            // detect what modifier keys are pressed, using   
            // Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers  
            // since Keyboard.Modifiers does not correctly get the state of the   
            // modifier keys when the application does not have focus
            this.Key = (Keys)keyCode;
            this.Alt = (Control.ModifierKeys & Keys.Alt) != 0;
            this.Ctrl = (Control.ModifierKeys & Keys.Control) != 0;
            this.Shift = (Control.ModifierKeys & Keys.Shift) != 0;
        }
    }
    /// <summary>
    /// 处理Hook的子程序
    /// </summary>
    /// <param name="code"></param>
    /// <param name="wParam"></param>
    /// <param name="lParam"></param>
    /// <returns></returns>
    public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
    /// <summary>
    /// 处理收到的消息句柄
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void HookEventHandler(object sender, HookEventArgs e);
    #endregion

    /// <summary>
    /// 键盘Hook类
    /// </summary>
    public class KeyboardHook {
        #region 对外事件接口
        /// <summary>
        /// 按键按下事件
        /// </summary>
        public event HookEventHandler KeyDown;
        /// <summary>
        /// 按键弹出事件
        /// </summary>
        public event HookEventHandler KeyUp;
        #endregion

        #region Windows API对象
        /// <summary>
        /// 安装钩子
        /// </summary>
        /// <param name="idHook"></param>
        /// <param name="lpfn"></param>
        /// <param name="hInstance"></param>
        /// <param name="threadId"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int lpcode, HookProc lpfn, IntPtr hInstance, int threadId);

        /// <summary>
        /// 卸载钩子
        /// </summary>
        /// <param name="idHook"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(IntPtr idHook);

        /// <summary>
        /// 继续下一个钩子处理程序
        /// </summary>
        /// <param name="idHook"></param>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// 取得当前线程编号
        /// </summary>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        private static extern int GetCurrentThreadId();
        /// <summary>
        /// 获取扩展模块
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
        #endregion

        #region 私有成员对象
        private static IntPtr _hookHandle;
        #endregion

        /// <summary>
        /// 键盘勾子事件
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int HookCallback(int code, IntPtr wParam, IntPtr lParam) {
            if (code < 0)
                return CallNextHookEx(_hookHandle, code, wParam, lParam);

            var dataStruce = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));

            if (((int)wParam == WindowMessage.WM_KEYUP || (int)wParam == WindowMessage.WM_SYSKEYUP) && this.KeyUp != null) {
                this.KeyUp(this, new HookEventArgs(dataStruce.vkCode));
            }

            if (((int)wParam == WindowMessage.WM_KEYDOWN || (int)wParam == WindowMessage.WM_SYSKEYDOWN) && this.KeyDown != null) {
                this.KeyDown(this, new HookEventArgs(dataStruce.vkCode));
                if (dataStruce.vkCode == 44) return 0;
            }

            return 0;
        }

        /// <summary>
        /// 安装钩子
        /// </summary>
        public void Install(HookType hookType = HookType.WH_KEYBOARD_LL, HookProc callBack = null) {
            if (_hookHandle != IntPtr.Zero)
                return;
            var _hookFunction = new HookProc(HookCallback);

            var pInstance = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
            //var pInstance =Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);

            _hookHandle = (IntPtr)SetWindowsHookEx((int)hookType, _hookFunction, pInstance, 0);
            //_hookHandle = (IntPtr)SetWindowsHookEx((int)hookType, _hookFunction, IntPtr.Zero, GetCurrentThreadId());
        }

        /// <summary>
        /// 卸载钩子
        /// </summary>
        public void Uninstall() {
            if (_hookHandle != IntPtr.Zero) {
                UnhookWindowsHookEx(_hookHandle);
                _hookHandle = IntPtr.Zero;
            }
        }
    }
}